﻿
Public Class resolver
    Public Shared ReadOnly [global] As resolver = Nothing
    Private lock As duallock
    Private _fixed As Boolean

    Shared Sub New()
        [global] = New resolver()
    End Sub

    Private creator As Dictionary(Of Type, _do(Of Object)) = Nothing

    Private Shared Function defaultResolver(Of T)(ByVal i As T) As _do(Of T)
        Return Function() As T
                   Return i
               End Function
    End Function

    Public Sub fix()
        lock.writerWait()
        _fixed = True
        lock.writerRelease()
    End Sub

    Public Function fixed() As Boolean
        Return _fixed
    End Function

    Public Function size() As Int32
        Return creator.Count()
    End Function

    Public Sub register(ByVal T As Type, ByVal i As Object)
        assert(Not i Is Nothing)
        register(T, defaultResolver(i))
    End Sub

    Public Sub register(Of T)(ByVal i As T)
        assert(Not i Is Nothing)
        register(Of T)(defaultResolver(i))
    End Sub

    Public Sub register(ByVal T As Type, ByVal d As _do(Of Object))
        assert(Not d Is Nothing)
        lock.writerWait()
        If fixed() Then
            assert(False, "should not register after fix() has been called")
            Return
        End If
        creator(T) = d
        lock.writerRelease()
    End Sub

    Private Function assert_create(Of T2)(ByVal T As Type, ByVal d As _do(Of T2)) As Object
        assert(Not T Is Nothing)
        assert(Not d Is Nothing)
        Dim rtn As Object = Nothing
        rtn = d()
        If rtn Is Nothing AndAlso Not T.IsValueType() Then
            assert(False, strcat("failed to resolve ", T.FullName, " via ", invokeMethodName(d)))
        End If
        Return rtn
    End Function

    Public Sub register(Of T)(ByVal d As _do(Of T))
        assert(Not d Is Nothing)
        Dim type As Type = Nothing
        type = GetType(T)
        register(type, Function() As Object
                           Return assert_create(type, d)
                       End Function)
    End Sub

    Public Function resolve(ByVal T As Type) As Object
        Dim create As _do(Of Object) = Nothing
        create = resolver(T)
        If create Is Nothing Then
            assert(False, "type ", T.FullName, " has not been registered.")
            Return Nothing
        Else
            Return assert_create(T, create)
        End If
    End Function

    Public Function resolve(Of T)() As T
        Return convertor.cast(Of T)(resolve(GetType(T)))
    End Function

    Private Function hasResolverImpl(ByVal T As Type) As Boolean
        Return creator.ContainsKey(T)
    End Function

    Private Sub lockReader(ByVal d As void)
        assert(Not d Is Nothing)
        If fixed() Then
            d()
        Else
            lock.readerWait()
            d()
            lock.readerRelease()
        End If
    End Sub

    Public Function hasResolver(ByVal T As Type) As Boolean
        Dim rtn As Boolean
        lockReader(Sub()
                       rtn = hasResolverImpl(T)
                   End Sub)
        Return rtn
    End Function

    Public Function hasResolver(Of T)() As Boolean
        Return hasResolver(GetType(T))
    End Function

    Public Function resolver(ByVal T As Type) As _do(Of Object)
        Dim rtn As _do(Of Object) = Nothing
        lockReader(Sub()
                       If hasResolverImpl(T) Then
                           rtn = creator(T)
                       Else
                           rtn = Nothing
                       End If
                   End Sub)
        Return rtn
    End Function

    Public Function resolver(Of T)() As _do(Of Object)
        Return resolver(GetType(T))
    End Function

    Public Function [erase](ByVal T As Type) As Boolean
        If T Is Nothing Then
            Return False
        Else
            Dim rtn As Boolean
            lock.writerWait()
            rtn = creator.Remove(T)
            lock.writerRelease()
            Return rtn
        End If
    End Function

    Public Function [erase](Of T)() As Boolean
        Return [erase](GetType(T))
    End Function

    Public Sub New()
        creator = New Dictionary(Of Type, _do(Of Object))()
        lock = New duallock()
    End Sub
End Class

Public Module _resolver
    Public Function resolve(Of T)() As T
        Return resolver.global.resolve(Of T)()
    End Function

    Public Function resolve_or_alloc(Of T)() As T
        If resolver.global.hasResolver(Of T)() Then
            Return resolve(Of T)()
        Else
            Return alloc(Of T)()
        End If
    End Function

    Public Function check_resolve_or_alloc(Of T As Class)(ByVal i As T) As T
        If i Is Nothing Then
            Return resolve_or_alloc(Of T)()
        Else
            Return i
        End If
    End Function
End Module
